﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.Modeling;

namespace VsxFactory.Modeling.Strategies
{
    public abstract class ProjectGenerationStrategy : IProjectGenerationStrategy
    {
        protected IStrategiesHost Host { get; private set; }
        protected Store Store { get; private set; }

        public interface IProjectSpec
        {
            string VSTemplate { get; }
            Guid ProjectGuid { get; set; }
            string Name { get; }
            string AssemblyName { get; }
            string Namespace { get; }
            string Folder { get; }
        }

        public virtual void Initialize(IStrategiesHost host, Store store)
        {
            System.Diagnostics.Contracts.Contract.Requires(store != null);
            System.Diagnostics.Contracts.Contract.Requires(host != null);
            Host = host;
            Store = store;
        }        

        public void GenerateProjects()
        {
            var context = Host.GetStrategyContext(null, null, CodeGenerationEvent.None);
            context.ClearErrors();

            // On ne prend que les projets non encore générés
            var projects = (from project in GetProjects(Store)
                            where !String.IsNullOrEmpty(project.VSTemplate) && !String.IsNullOrEmpty(project.Name)
                            let pn = context.SolutionExplorer.CurrentSolution.GetProject(project.ProjectGuid)
                            where pn == null
                            select project).ToList();
            if (projects.Count == 0)
                return;

            try
            {
                // TODO using (var disabler = new VsxFactory.Modeling.VisualStudio.Synchronization.ProjectEventsDisabler(context.SolutionExplorer, "*")) 
                {
                    OnBeforeGeneration(context, Store);

                    // Création des projets et mise à jour de leur guid dans le modéle.
                    using (Transaction transaction = Store.TransactionManager.BeginTransaction("Generate"))
                    {
                        foreach (var project in projects)
                        {
                            context.WriteLogMessage("Generate project {0}", project.AssemblyName);
                            try
                            {
                                var p = context.SolutionExplorer.CurrentSolution.CreateProject(
                                    context.Settings.GetPackageFolder(VsxFactory.Modeling.Repository.PackageKind.ProjectTemplate), 
                                    project.Folder, 
                                    project.Name,
                                    project.AssemblyName, 
                                    project.VSTemplate, 
                                    project.Namespace);
                                project.ProjectGuid = p.ProjectGuid;

                                OnProjectGenerated(context, project, p);
                            }
                            catch (Exception ex)
                            {
                                context.WriteError("Generation failed for project {0} - Error : {1}", project.AssemblyName, ex.Message);
                            }
                        }

                        // Ajout des références de projet, une fois qu'ils sont tous créés   
                        foreach (var reference in GetReferences(Store))
                        {
                            var sourceProjectNode = context.SolutionExplorer.CurrentSolution.GetProject(reference.Item1);
                            var targetProjectNode = context.SolutionExplorer.CurrentSolution.GetProject(reference.Item2);
                            if (sourceProjectNode != null && targetProjectNode != null)
                            {
                                context.WriteLogMessage("Add project reference {0} to project {1}", targetProjectNode.Name, sourceProjectNode.Name);
                                sourceProjectNode.AddProjectReference(targetProjectNode.ProjectGuid);
                            }
                        }

                        transaction.Commit();
                    }
                }
            }
            finally
            {
            }
        }

        protected abstract IEnumerable<Tuple<Guid, Guid>> GetReferences(Store store);

        protected abstract void OnProjectGenerated(IStrategyContext context, IProjectSpec project, VsxFactory.Modeling.VisualStudio.IVsProject projectNode);

        protected abstract void OnBeforeGeneration(IStrategyContext context, Store store);

        protected abstract IEnumerable<IProjectSpec> GetProjects(Store store);
    }


}
